/*
 * Example code to identify and communicate with the board using "native" USB.
 * Copyright (C) 2011 Jorge Pinto <casainho@gmail.com>
 * 
 * Here is the "lsusb -v" for my Arduino:

Bus 002 Device 002: ID 0403:6001 Future Technology Devices
International, Ltd FT232 USB-Serial (UART) IC
Device Descriptor:
 bLength                18
 bDescriptorType         1
 bcdUSB               2.00
 bDeviceClass            0 (Defined at Interface level)
 bDeviceSubClass         0
 bDeviceProtocol         0
 bMaxPacketSize0         8
 idVendor           0x0403 Future Technology Devices International, Ltd
 idProduct          0x6001 FT232 USB-Serial (UART) IC
 bcdDevice            6.00
 iManufacturer           1 FTDI
 iProduct                2 FT232R USB UART
 iSerial                 3 A9007KEw
 bNumConfigurations      1
 Configuration Descriptor:
   bLength                 9
   bDescriptorType         2
   wTotalLength           32
   bNumInterfaces          1
   bConfigurationValue     1
   iConfiguration          0
   bmAttributes         0xa0
     (Bus Powered)
     Remote Wakeup
   MaxPower               90mA
   Interface Descriptor:
     bLength                 9
     bDescriptorType         4
     bInterfaceNumber        0
     bAlternateSetting       0
     bNumEndpoints           2
     bInterfaceClass       255 Vendor Specific Class
     bInterfaceSubClass    255 Vendor Specific Subclass
     bInterfaceProtocol    255 Vendor Specific Protocol
     iInterface              2 FT232R USB UART
     Endpoint Descriptor:
       bLength                 7
       bDescriptorType         5
       bEndpointAddress     0x81  EP 1 IN
       bmAttributes            2
         Transfer Type            Bulk
         Synch Type               None
         Usage Type               Data
       wMaxPacketSize     0x0040  1x 64 bytes
       bInterval               0
     Endpoint Descriptor:
       bLength                 7
       bDescriptorType         5
       bEndpointAddress     0x02  EP 2 OUT
       bmAttributes            2
         Transfer Type            Bulk
         Synch Type               None
         Usage Type               Data
       wMaxPacketSize     0x0040  1x 64 bytes
       bInterval               0
Device Status:     0x0000
 (Bus Powered)

 *
 *
 * This code is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This code is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <stdio.h>
#include <sys/types.h>
#include <string.h>

#include <libusb/libusb.h>

#define IMANUFACTURE    "FTDI"
#define IPRODUCT        "FT232R USB UART"
#define BULK_EP_OUT     0x02
#define BULK_EP_IN      0x81
#define CONFIGURATION   1
#define INTERFACE       0

int main(void)
{
  int r = 1;
  libusb_context *context;
  struct libusb_device_handle *handle = NULL;
  libusb_device **devs;
  libusb_device *dev;
  ssize_t cnt;
  int e = 0;
  char str1[256], str2[256];
  char found = 0;


  /* Init libusb */
  r = libusb_init(&context);
  if (r < 0)
  {
    fprintf(stderr, "failed to initialise libusb\n");
    return 1;
  }

  libusb_set_debug(context, 3);

  /* Get a list os USB devices */
  cnt = libusb_get_device_list(NULL, &devs);
  if (cnt < 0)
  {
   printf("there are none USB devices on bus\n");
   return -1;
  }

  {
    int i = 0;
    while ((dev = devs[i++]) != NULL)
    {
      struct libusb_device_descriptor desc;
      /* Get the device descriptor */
      r = libusb_get_device_descriptor(dev, &desc);
      if (r < 0)
      {
        printf("failed to get device descriptor\n");
        break;
      }

      /* Open the device */
      e = libusb_open(dev, &handle);
      if (e < 0)
      {
        printf("error opening device\n");
        break;
      }

      /* Get the iManufacturer to str1 */
      e = libusb_get_string_descriptor_ascii(handle, desc.iManufacturer, (unsigned char*) str1, sizeof(str1));
      if (e < 0)
      {
        libusb_close(handle);
        break;
      }

      /* Get the iProduct to str2 */
      e = libusb_get_string_descriptor_ascii(handle, desc.iProduct, (unsigned char*) str2, sizeof(str2));
      if (e < 0)
      {
        libusb_close(handle);
        break;
      }

      if (!(strcmp(str1, IMANUFACTURE) && strcmp(str2, IPRODUCT)))
      {
        found = 1;
        break;
      }
    }
  }

  if (!found)
  {
    printf("FTDI USB ic from Arduino NOT found\n");
    return 1;
  }
  else
    printf("FTDI USB ic from Arduino found\n");


  libusb_set_configuration(handle, CONFIGURATION);

  e = libusb_kernel_driver_active(handle, INTERFACE);
  if (e != 0)
  {
    printf("removing the attached interface %d from kernel\n", INTERFACE);

    e = libusb_detach_kernel_driver(handle, INTERFACE);
    if (e != 0)
    {
      printf("error detach kernel: %d\n", e);
      return 1;
    }

    e = libusb_claim_interface(handle, INTERFACE);
    if (e != 0)
    {
      printf("error clamming the interface: %d\n", e);
      return 1;
    }
  }


  /* Send commands to board */
  int bytes_read;
  int nbytes = 256;
  unsigned char *my_string, *my_string1;
  int transferred;
  my_string = (unsigned char *) malloc (nbytes + 1);
  my_string1 = (unsigned char *) malloc (nbytes + 1);

  printf("Write any commands you wish to send to board and hit ENTER\nPress CTRL-C at anytime to abort\n");
  while (1)
  {
    bytes_read = getline ((char ** __restrict__) &my_string, (size_t * __restrict__) &nbytes, stdin);
    if (bytes_read)
    {
      e = libusb_bulk_transfer(handle, BULK_EP_OUT, my_string, bytes_read, &transferred, 5000);
      if (e != 0)
      {
        printf("error bulk transfer OUT: %d\n", e);
      }
      else
      {
        printf("Sent %d bytes with string: %s", transferred, my_string);
      }

      e = libusb_bulk_transfer(handle, BULK_EP_IN, my_string1, 256, &transferred, 5000);
      if (e != 0)
      {
        printf("error bulk transfer IN: %d\n", e);
      }
      else
      {
        printf("Received %d bytes with string: ", transferred);
      }

      while (transferred-- && e == 0)
      {
        printf("%c", *(my_string1++));
      }
    }
  }

  libusb_close(handle);
  libusb_exit(NULL);
  return 0;
}

